// https://learn.microsoft.com/en-us/typography/opentype/spec/os2

// The OS/2 table consists of a set of metrics and other data that are required in OpenType fonts.

use super::*;

#[derive(Debug, Clone)]
pub struct Table {
	pub version: u16, // 0x0005
	pub x_avg_char_width: FWORD,
	pub us_weight_class: u16,
	pub us_width_class: u16,
	pub fs_type: u16,
	pub y_subscript_xsize: FWORD,
	pub y_subscript_ysize: FWORD,
	pub y_subscript_xoffset: FWORD,
	pub y_subscript_yoffset: FWORD,
	pub y_superscript_xsize: FWORD,
	pub y_superscript_ysize: FWORD,
	pub y_superscript_xoffset: FWORD,
	pub y_superscript_yoffset: FWORD,
	pub y_strikeout_size: FWORD,
	pub y_strikeout_position: FWORD,
	pub s_family_class: FWORD,
	pub panose: [u8; 10],
	pub ul_unicode_range1: u32, // Bits 0 – 31
	pub ul_unicode_range2: u32, // Bits 32 – 63
	pub ul_unicode_range3: u32, // Bits 64 – 95
	pub ul_unicode_range4: u32, // Bits 96 – 127
	pub ach_vend_id: Tag,
	pub fs_selection: u16,
	pub us_first_char_index: u16,
	pub us_last_char_index: u16,
	pub s_typo_ascender: FWORD,
	pub s_typo_descender: FWORD,
	pub s_typo_line_gap: FWORD,
	pub us_win_ascent: UFWORD,
	pub us_win_descent: UFWORD,
	pub ul_code_page_range1: u32, // Bits 0 – 31
	pub ul_code_page_range2: u32, // Bits 32 – 63
	pub sx_height: FWORD,
	pub s_cap_height: FWORD,
	pub us_default_char: u16,
	pub us_break_char: u16,
	pub us_max_context: u16,
	pub us_lower_optical_point_size: u16,
	pub us_upper_optical_point_size: u16,
}
impl Table {
	pub fn parse(data: &[u8]) -> Option<Self> {
		let mut s = Stream::new(data);
		let version = s.read::<u16>()?;
		let this = Table {
			version,
			x_avg_char_width: s.read()?,
			us_weight_class: s.read()?,
			us_width_class: s.read()?,
			fs_type: s.read()?,
			y_subscript_xsize: s.read()?,
			y_subscript_ysize: s.read()?,
			y_subscript_xoffset: s.read()?,
			y_subscript_yoffset: s.read()?,
			y_superscript_xsize: s.read()?,
			y_superscript_ysize: s.read()?,
			y_superscript_xoffset: s.read()?,
			y_superscript_yoffset: s.read()?,
			y_strikeout_size: s.read()?,
			y_strikeout_position: s.read()?,
			s_family_class: s.read()?,
			panose: {
				let mut panose = [0; 10];
				for v in panose.iter_mut() {
					*v = s.read()?;
				}
				panose
			},
			ul_unicode_range1: s.read()?,
			ul_unicode_range2: s.read()?,
			ul_unicode_range3: s.read()?,
			ul_unicode_range4: s.read()?,
			ach_vend_id: s.read()?,
			fs_selection: s.read()?,
			us_first_char_index: s.read()?,
			us_last_char_index: s.read()?,
			s_typo_ascender: s.read()?,
			s_typo_descender: s.read()?,
			s_typo_line_gap: s.read()?,
			us_win_ascent: s.read()?,
			us_win_descent: s.read()?,
			ul_code_page_range1: if version >= 1 { s.read()? } else { 0 },
			ul_code_page_range2: if version >= 1 { s.read()? } else { 0 },
			sx_height: if version >= 2 { s.read()? } else { 0 },
			s_cap_height: if version >= 2 { s.read()? } else { 0 },
			us_default_char: if version >= 2 { s.read()? } else { 0 },
			us_break_char: if version >= 2 { s.read()? } else { 0 },
			us_max_context: if version >= 2 { s.read()? } else { 0 },
			us_lower_optical_point_size: if version >= 5 { s.read()? } else { 0 },
			us_upper_optical_point_size: if version >= 5 { s.read()? } else { 0 },
		};
		Some(this)
	}
	#[inline]
	pub fn subscript_metrics(&self) -> ScriptMetrics {
		ScriptMetrics {
			x_size: self.y_subscript_xsize,
			y_size: self.y_subscript_ysize,
			x_offset: self.y_subscript_xoffset,
			y_offset: self.y_subscript_yoffset,
		}
	}
	#[inline]
	pub fn superscript_metrics(&self) -> ScriptMetrics {
		ScriptMetrics {
			x_size: self.y_subscript_xsize,
			y_size: self.y_subscript_ysize,
			x_offset: self.y_subscript_xoffset,
			y_offset: self.y_superscript_yoffset,
		}
	}
	#[inline]
	pub fn strikeout_metrics(&self) -> LineMetrics {
		LineMetrics {
			position: self.y_strikeout_position,
			thickness: self.y_strikeout_size,
		}
	}
	#[inline]
	pub fn unicode_ranges(&self) -> UnicodeRanges {
		let n1 = self.ul_unicode_range1 as u128;
		let n2 = self.ul_unicode_range2 as u128;
		let n3 = self.ul_unicode_range3 as u128;
		let n4 = self.ul_unicode_range4 as u128;
		UnicodeRanges(n4 << 96 | n3 << 64 | n2 << 32 | n1)
	}
}

/// A line metrics.
///
/// Used for underline and strikeout.
#[repr(C)]
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct LineMetrics {
	/// Line position.
	pub position: i16,
	/// Line thickness.
	pub thickness: i16,
}

/// A script metrics used by subscript and superscript.
#[repr(C)]
#[derive(Clone, Copy, Eq, PartialEq, Debug, Hash)]
pub struct ScriptMetrics {
	/// Horizontal face size.
	pub x_size: i16,
	/// Vertical face size.
	pub y_size: i16,
	/// X offset.
	pub x_offset: i16,
	/// Y offset.
	pub y_offset: i16,
}

/// [Unicode Ranges](https://docs.microsoft.com/en-us/typography/opentype/spec/os2#ur).
#[derive(Clone, Copy, Default, Debug)]
pub struct UnicodeRanges(pub u128);
impl UnicodeRanges {
	/// Checks if ranges contain the specified character.
	pub fn contains_char(&self, c: char) -> bool {
		let range = Self::char_range_index(c);
		if range >= 0 {
			self.0 & (1 << range as u128) != 0
		} else {
			false
		}
	}
	pub fn char_range_index(c: char) -> i8 {
		match c as u32 {
			0x0000..=0x007F => 0,
			0x0080..=0x00FF => 1,
			0x0100..=0x017F => 2,
			0x0180..=0x024F => 3,
			0x0250..=0x02AF => 4,
			0x1D00..=0x1DBF => 4,
			0x02B0..=0x02FF => 5,
			0xA700..=0xA71F => 5,
			0x0300..=0x036F => 6,
			0x1DC0..=0x1DFF => 6,
			0x0370..=0x03FF => 7,
			0x2C80..=0x2CFF => 8,
			0x0400..=0x052F => 9,
			0x2DE0..=0x2DFF => 9,
			0xA640..=0xA69F => 9,
			0x0530..=0x058F => 10,
			0x0590..=0x05FF => 11,
			0xA500..=0xA63F => 12,
			0x0600..=0x06FF => 13,
			0x0750..=0x077F => 13,
			0x07C0..=0x07FF => 14,
			0x0900..=0x097F => 15,
			0x0980..=0x09FF => 16,
			0x0A00..=0x0A7F => 17,
			0x0A80..=0x0AFF => 18,
			0x0B00..=0x0B7F => 19,
			0x0B80..=0x0BFF => 20,
			0x0C00..=0x0C7F => 21,
			0x0C80..=0x0CFF => 22,
			0x0D00..=0x0D7F => 23,
			0x0E00..=0x0E7F => 24,
			0x0E80..=0x0EFF => 25,
			0x10A0..=0x10FF => 26,
			0x2D00..=0x2D2F => 26,
			0x1B00..=0x1B7F => 27,
			0x1100..=0x11FF => 28,
			0x1E00..=0x1EFF => 29,
			0x2C60..=0x2C7F => 29,
			0xA720..=0xA7FF => 29,
			0x1F00..=0x1FFF => 30,
			0x2000..=0x206F => 31,
			0x2E00..=0x2E7F => 31,
			0x2070..=0x209F => 32,
			0x20A0..=0x20CF => 33,
			0x20D0..=0x20FF => 34,
			0x2100..=0x214F => 35,
			0x2150..=0x218F => 36,
			0x2190..=0x21FF => 37,
			0x27F0..=0x27FF => 37,
			0x2900..=0x297F => 37,
			0x2B00..=0x2BFF => 37,
			0x2200..=0x22FF => 38,
			0x2A00..=0x2AFF => 38,
			0x27C0..=0x27EF => 38,
			0x2980..=0x29FF => 38,
			0x2300..=0x23FF => 39,
			0x2400..=0x243F => 40,
			0x2440..=0x245F => 41,
			0x2460..=0x24FF => 42,
			0x2500..=0x257F => 43,
			0x2580..=0x259F => 44,
			0x25A0..=0x25FF => 45,
			0x2600..=0x26FF => 46,
			0x2700..=0x27BF => 47,
			0x3000..=0x303F => 48,
			0x3040..=0x309F => 49,
			0x30A0..=0x30FF => 50,
			0x31F0..=0x31FF => 50,
			0x3100..=0x312F => 51,
			0x31A0..=0x31BF => 51,
			0x3130..=0x318F => 52,
			0xA840..=0xA87F => 53,
			0x3200..=0x32FF => 54,
			0x3300..=0x33FF => 55,
			0xAC00..=0xD7AF => 56,
			// Ignore Non-Plane 0 (57), since this is not a real range.
			0x10900..=0x1091F => 58,
			0x4E00..=0x9FFF => 59,
			0x2E80..=0x2FDF => 59,
			0x2FF0..=0x2FFF => 59,
			0x3400..=0x4DBF => 59,
			0x20000..=0x2A6DF => 59,
			0x3190..=0x319F => 59,
			0xE000..=0xF8FF => 60,
			0x31C0..=0x31EF => 61,
			0xF900..=0xFAFF => 61,
			0x2F800..=0x2FA1F => 61,
			0xFB00..=0xFB4F => 62,
			0xFB50..=0xFDFF => 63,
			0xFE20..=0xFE2F => 64,
			0xFE10..=0xFE1F => 65,
			0xFE30..=0xFE4F => 65,
			0xFE50..=0xFE6F => 66,
			0xFE70..=0xFEFF => 67,
			0xFF00..=0xFFEF => 68,
			0xFFF0..=0xFFFF => 69,
			0x0F00..=0x0FFF => 70,
			0x0700..=0x074F => 71,
			0x0780..=0x07BF => 72,
			0x0D80..=0x0DFF => 73,
			0x1000..=0x109F => 74,
			0x1200..=0x139F => 75,
			0x2D80..=0x2DDF => 75,
			0x13A0..=0x13FF => 76,
			0x1400..=0x167F => 77,
			0x1680..=0x169F => 78,
			0x16A0..=0x16FF => 79,
			0x1780..=0x17FF => 80,
			0x19E0..=0x19FF => 80,
			0x1800..=0x18AF => 81,
			0x2800..=0x28FF => 82,
			0xA000..=0xA48F => 83,
			0xA490..=0xA4CF => 83,
			0x1700..=0x177F => 84,
			0x10300..=0x1032F => 85,
			0x10330..=0x1034F => 86,
			0x10400..=0x1044F => 87,
			0x1D000..=0x1D24F => 88,
			0x1D400..=0x1D7FF => 89,
			0xF0000..=0xFFFFD => 90,
			0x100000..=0x10FFFD => 90,
			0xFE00..=0xFE0F => 91,
			0xE0100..=0xE01EF => 91,
			0xE0000..=0xE007F => 92,
			0x1900..=0x194F => 93,
			0x1950..=0x197F => 94,
			0x1980..=0x19DF => 95,
			0x1A00..=0x1A1F => 96,
			0x2C00..=0x2C5F => 97,
			0x2D30..=0x2D7F => 98,
			0x4DC0..=0x4DFF => 99,
			0xA800..=0xA82F => 100,
			0x10000..=0x1013F => 101,
			0x10140..=0x1018F => 102,
			0x10380..=0x1039F => 103,
			0x103A0..=0x103DF => 104,
			0x10450..=0x1047F => 105,
			0x10480..=0x104AF => 106,
			0x10800..=0x1083F => 107,
			0x10A00..=0x10A5F => 108,
			0x1D300..=0x1D35F => 109,
			0x12000..=0x123FF => 110,
			0x12400..=0x1247F => 110,
			0x1D360..=0x1D37F => 111,
			0x1B80..=0x1BBF => 112,
			0x1C00..=0x1C4F => 113,
			0x1C50..=0x1C7F => 114,
			0xA880..=0xA8DF => 115,
			0xA900..=0xA92F => 116,
			0xA930..=0xA95F => 117,
			0xAA00..=0xAA5F => 118,
			0x10190..=0x101CF => 119,
			0x101D0..=0x101FF => 120,
			0x102A0..=0x102DF => 121,
			0x10280..=0x1029F => 121,
			0x10920..=0x1093F => 121,
			0x1F030..=0x1F09F => 122,
			0x1F000..=0x1F02F => 122,
			_ => -1,
		}
	}
}
